package com.zs.usc.common.log;

import cn.hutool.core.util.ArrayUtil;
import com.zs.usc.common.BusinessException;
import com.zs.usc.common.BaseAuthController;
import com.zs.usc.common.RestRst;
import com.zs.usc.common.RestCode;
import com.zs.usc.common.log.enums.LogTypes;
import com.zs.usc.entity.LogInfo;
import com.zs.usc.service.ILogService;
import com.zs.usc.utils.JsonUtils;
import com.zs.usc.utils.ReqUtils;
import com.zs.usc.utils.UUIDUtils;
import com.zs.usc.vo.CurrentUserInfo;
import org.apache.commons.collections4.MapUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;


/**
 * aop日志记录
 */
@Aspect
@Component // 优于
public class SysLogAspect {


    @Autowired
    private HttpServletRequest request;

    @Autowired
    ILogService logService;

    //在注解的位置切入代码
    @Around("@annotation(sysMethodLog)")
    public Object doLog(ProceedingJoinPoint joinPoint, SysMethodLog sysMethodLog) throws Throwable {
        Object respRst = null;
        Throwable respExp = null;
        boolean finalExecute = true;
        try {
            //执行目标方法
            respRst = joinPoint.proceed();
            // 认证拦截未通过     // respRst 1.未认证
            if (!Objects.isNull(respRst)
                    && respRst instanceof RestRst
                    && (((RestRst) respRst).getStatus() == RestCode.ERROR_ACCOUNT_NOT_LOGIN.getCode())) {
                // 日志不执行
                finalExecute = false;
                return respRst;
            }
        } catch (Throwable throwable) {
            respExp = throwable;
            throw new BusinessException(throwable);
        } finally {
            if (finalExecute) {
                // 最终执行代码 2.无返回值 3.正常返回值
                LogInfo logInfo = getLogInfo(joinPoint, sysMethodLog, respRst, respExp);
                if (!Objects.isNull(logInfo)) {
                    // todo 异步处理日志
                    System.out.println("logInfo = " + logInfo);
                    logService.save(logInfo);
                }
            }
            // finally语句中有异常抛出，则整个try-catch抛出异常
            // finally语句中有return,则try-catch中的return、异常都会被忽略
//            return respRst;
        }
        return respRst;

    }

    /**
     * 获取日志信息
     */
    public LogInfo getLogInfo(JoinPoint joinPoint, SysMethodLog sysMethodLog, Object respRst, Throwable respExp) {
        //系统操作，非异常时不记录
        if (Objects.isNull(respExp) && LogTypes.SYSTEM_OPERATION.equals(sysMethodLog.logTypes()))
            return null;

        // 日志信息
        LogInfo logInfo = new LogInfo();
        logInfo.setLogId(UUIDUtils.generate());
        logInfo.setBrowser(ReqUtils.getBrowserName(request));
        logInfo.setOperateSystem(ReqUtils.getOsName(request));
        logInfo.setIp(ReqUtils.getIp(request));

        // 获取SysModuleLog
        SysModuleLog sysModuleLog = joinPoint.getTarget().getClass().getAnnotation(SysModuleLog.class);
        // 模块日志不为空
        if (!Objects.isNull(sysModuleLog)) {
            logInfo.setModuleName(sysModuleLog.name());
        }
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();
        // 类名+方法名
        logInfo.setMethodName(joinPoint.getTarget().getClass().getName() + "." + method.getName() + "()");

        logInfo.setRequestMethod(request.getMethod());

        logInfo.setRequestUri(request.getRequestURI());

        logInfo.setOperateType(sysMethodLog.operationType());
        // 请求参数
        logInfo.setRequestParam(ReqUtils.getReqParam(joinPoint));
        logInfo.setOperateType(sysMethodLog.operationType());
        logInfo.setLogType(sysMethodLog.logTypes());

        // 发生异常
        if (!Objects.isNull(respExp)) {
            //异常信息
            logInfo.setStatusCode(RestCode.ERROR_BUSINESS_COMMON.getCode());
            logInfo.setStatusName("失败");
            if (!Objects.isNull(respExp)) {
                StackTraceElement[] stackTraces = respExp.getStackTrace();
                if (ArrayUtil.isNotEmpty(stackTraces)) {
                    logInfo.setExceptionMessage(respExp.getClass().getName() + ":" + respExp.getMessage() + "\t" + stackTraces[0].toString());
                }
            }
            // 无异常 有返回值
        } else if (!Objects.isNull(respRst)) {
            //返回信息
            String responseJson = JsonUtils.objectToJson(respRst);
            logInfo.setResponseParam(responseJson.length() > 1900 ? responseJson.substring(0, 1900) + "..." : responseJson);
            RestRst restRst = JsonUtils.jsonToPojo(responseJson, RestRst.class);
            logInfo.setStatusCode(restRst.getStatus() == RestCode.SUCCESS.getCode() ?
                    RestCode.SUCCESS.getCode() : RestCode.ERROR_BUSINESS_COMMON.getCode());
            logInfo.setStatusName(RestCode.SUCCESS.getCode() == restRst.getStatus() ? "成功" : "失败");
        } else {
            // 无异常 无返回值
            logInfo.setStatusCode(RestCode.SUCCESS.getCode());
            logInfo.setResponseParam("Void方法");
            logInfo.setStatusName("成功");
        }


        //设置登录账号
        if (LogTypes.LOGIN.equals(logInfo.getLogType())) {
            Map<String, String> map = JsonUtils.jsonToPojo(logInfo.getRequestParam(), Map.class);
            if (MapUtils.isNotEmpty(map) && map.containsKey("username")) {
                logInfo.setUserName(map.get("username"));
            }
        }
        //  为空:1.登录操作 2.不需要登录就可访问的接口
        CurrentUserInfo currentUserInfo = BaseAuthController.get();
        if (!Objects.isNull(currentUserInfo)) {
            logInfo.setUserId(currentUserInfo.getUid());
            logInfo.setUserName(currentUserInfo.getUsername());
        }

        return logInfo;
    }


}

